package 二分查找;

/**
 * 34. 在排序数组中查找元素的第一个和最后一个位置
 * 给你一个按照非递减顺序排列的整数数组 nums，和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
 * 如果数组中不存在目标值 target，返回 [-1, -1]。
 * 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
 *
 * 思路： 采用两次二分查找，找到最左边 和 最右边的结果下标，返回即可
 *  1:二分查找（Binary Search）：
 *      首先，我们需要在排序数组中找到目标值的范围。由于数组已排序，可以使用二分查找来高效地找到目标值的左边界和右边界。
 *      具体来说，需要两次二分：
 *          第一次查找目标值的左边界（第一次出现的位置）。
 *          第二次查找目标值的右边界（最后一次出现的位置）。
 *  2：左边界查找：在二分查找过程中，当找到一个等于目标值的元素时，继续向左查找，以确定左边界。
 *     右边界查找：在二分查找过程中，当找到一个等于目标值的元素时，继续向右查找，以确定右边界。
 *  3：边界条件处理：如果数组为空或目标值不在数组中，返回 [-1, -1]。
 */
public class L_34 {
    public int[] searchRange(int[] nums, int target) {
        int[] result = new int[]{-1,-1};
        if (nums.length == 0 || target < nums[0] || target > nums[nums.length - 1]){
            return result;
        }
        int left = findLeft(nums,target);
        int right = findRight(nums,target);
        return new int[]{left,right};
    }

    // 查找到最左边的结果
    private int findLeft(int[] nums, int target){
        int left=0,right= nums.length - 1;
        int candidate = -1; // 记录下左边界
        while (left <= right){
            int mid = left + (right - left) / 2;
            // 如果找到目标值，则记录下左边界，继续向左查找
            if (nums[mid] == target){
                candidate = mid;
                right = mid - 1;
            }else if (nums[mid] < target){
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        return candidate;
    }

    // 查找到最右边的结果
    private int findRight(int[] nums, int target){
        int left = 0, right = nums.length - 1;
        int candidate = -1; // 记录下右边界
        while (left <= right){
            int mid = left + (right - left) / 2;
            if (nums[mid] == target){
                candidate = mid;
                left = mid + 1;
            }else if (nums[mid] < target){
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        return candidate;
    }
}
